import aiohttp
import asyncio
from fastapi import HTTPException
from config import Config
import logging
import time
from typing import Optional, Dict, Any, Union

class BaseService:
    """基础服务类，提供通用API调用功能"""
    def __init__(self, base_url=None):
        self.base_url = base_url or Config.TARGET_API_BASE_URL
        # 连接器将在第一次调用时初始化
        self._connector: Optional[aiohttp.TCPConnector] = None
        # 创建超时配置
        self.timeout = aiohttp.ClientTimeout(total=10)
        # 会话将在第一次调用时初始化
        self._session: Optional[aiohttp.ClientSession] = None
    
    async def get_session(self) -> aiohttp.ClientSession:
        """获取或创建aiohttp会话"""
        if self._session is None or self._session.closed:
            # 延迟创建连接器
            if self._connector is None:
                self._connector = aiohttp.TCPConnector(
                    limit=20,  # 总连接池大小
                    limit_per_host=20,  # 每个主机的连接数
                    ttl_dns_cache=300,  # DNS缓存时间
                    use_dns_cache=True,
                )
            self._session = aiohttp.ClientSession(
                connector=self._connector,
                timeout=self.timeout
            )
        return self._session
    
    async def close_session(self):
        """关闭会话和连接器"""
        if self._session and not self._session.closed:
            await self._session.close()
            self._session = None
        
        if self._connector and not self._connector.closed:
            await self._connector.close()
            self._connector = None
        
    async def call_api(self, endpoint: str, params: Union[Dict[str, Any], str], method: str = "post", stream: bool = False, max_retries: int = 3, base_url: Optional[str] = None) -> Union[Dict[str, Any], aiohttp.ClientResponse]:
        """
        异步通用API调用方法
        :param endpoint: API端点路径
        :param params: 请求参数，可以是字典或字符串（对于某些特殊API）
        :param method: HTTP方法(get/post)
        :param stream: 是否流式响应
        :param max_retries: 最大重试次数
        :param base_url: 可选的基础URL，如果提供则覆盖实例的base_url
        :return: 当stream=False时返回Dict[str, Any]，当stream=True时返回aiohttp.ClientResponse
        """
        start_time = time.time()
        logging.info(f"[步骤2-API调用] 开始异步API调用")
        
        # 使用传入的base_url或实例的base_url
        url = f"{base_url or self.base_url}{endpoint}"
        headers = {"Content-Type": "application/json"}
        session = await self.get_session()
        
        # 重试逻辑
        for attempt in range(max_retries + 1):
            try:
                if method.lower() == "get":
                    response = await session.get(url, params=params, headers=headers)
                    try:
                        if stream:
                            # 对于流式响应，返回响应对象
                            end_time = time.time()
                            logging.info(f"[步骤2-API调用] 异步API调用耗时: {end_time - start_time:.2f}秒")
                            return response
                        else:
                            # 对于非流式响应，返回JSON数据
                            result = await response.json()
                            end_time = time.time()
                            logging.info(f"[步骤2-API调用] 异步API调用耗时: {end_time - start_time:.2f}秒")
                            logging.info(f"[步骤2-API调用] 响应状态码: {response.status}")
                            logging.info(f"[步骤2-API调用] 响应头: {dict(response.headers)}")
                            # 检查HTTP状态码
                            response.raise_for_status()
                            return result
                    finally:
                        # 如果不是流式响应，确保关闭响应
                        if not stream:
                            response.close()
                else:
                    # 处理POST请求
                    if isinstance(params, str):
                        # 如果params是字符串，直接作为请求体
                        response = await session.post(url, data=params, headers=headers)
                    else:
                        # 如果params是字典，使用json参数
                        response = await session.post(url, json=params, headers=headers)
                    try:
                        if stream:
                            # 对于流式响应，返回响应对象
                            end_time = time.time()
                            logging.info(f"[步骤2-API调用] 异步API调用耗时: {end_time - start_time:.2f}秒")
                            return response
                        else:
                            # 对于非流式响应，返回JSON数据
                            result = await response.json()
                            end_time = time.time()
                            logging.info(f"[步骤2-API调用] 异步API调用耗时: {end_time - start_time:.2f}秒")
                            logging.info(f"[步骤2-API调用] 响应状态码: {response.status}")
                            logging.info(f"[步骤2-API调用] 响应头: {dict(response.headers)}")
                            # 检查HTTP状态码
                            response.raise_for_status()
                            return result
                    finally:
                        # 如果不是流式响应，确保关闭响应
                        if not stream:
                            response.close()
            
            except (aiohttp.ClientError, asyncio.TimeoutError) as e:
                if attempt < max_retries:
                    # 指数退避
                    backoff = min(2 ** attempt, 5)  # 最大等待5秒
                    logging.warning(f"[步骤2-API调用] 第{attempt + 1}次尝试失败，{backoff}秒后重试: {str(e)}")
                    await asyncio.sleep(backoff)
                else:
                    end_time = time.time()
                    logging.error(f"[步骤2-API调用] 异步API调用失败: {str(e)}, 耗时: {end_time - start_time:.2f}秒")
                    logging.error(f"[步骤2-API调用] 请求URL: {url}")
                    logging.error(f"[步骤2-API调用] 请求方法: {method.upper()}")
                    logging.error(f"[步骤2-API调用] 请求参数: {params}")
                    logging.error(f"[步骤2-API调用] 错误类型: {type(e).__name__}")
                    raise HTTPException(status_code=500, detail=f"API调用失败: {str(e)}")
            except aiohttp.ClientResponseError as e:
                # 处理HTTP错误响应
                end_time = time.time()
                logging.error(f"[步骤2-API调用] HTTP错误响应: {str(e)}, 耗时: {end_time - start_time:.2f}秒")
                logging.error(f"[步骤2-API调用] 请求URL: {url}")
                logging.error(f"[步骤2-API调用] 请求方法: {method.upper()}")
                logging.error(f"[步骤2-API调用] 请求参数: {params}")
                logging.error(f"[步骤2-API调用] HTTP状态码: {e.status}")
                logging.error(f"[步骤2-API调用] 错误类型: {type(e).__name__}")
                raise HTTPException(status_code=e.status, detail=f"HTTP错误: {str(e)}")
            except Exception as e:
                end_time = time.time()
                logging.error(f"[步骤2-API调用] 未知错误: {str(e)}, 耗时: {end_time - start_time:.2f}秒")
                logging.error(f"[步骤2-API调用] 请求URL: {url}")
                logging.error(f"[步骤2-API调用] 请求方法: {method.upper()}")
                logging.error(f"[步骤2-API调用] 请求参数: {params}")
                logging.error(f"[步骤2-API调用] 错误类型: {type(e).__name__}")
                raise HTTPException(status_code=500, detail=f"未知错误: {str(e)}")
        
        # 这行代码理论上不会执行到，但为了类型检查器的满足而添加
        raise HTTPException(status_code=500, detail="意外的代码路径")